package com;

import com.pojo.GameMap;
import com.pojo.Player;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.pojo.Player.resourcesType.*;

public class Game5 {

    // 模型参数调整
    private static Integer maxCapital = 0; //最优抉择收益值
    private static Integer initWaterNum = 200;    //初始携带食物
    private static Integer initFoodNum = 300;      //初始携带水
    private static final Integer initCapital = 10000; //起始资金
    private static final Integer waterWeight = 3; //单份水重量
    private static final Integer foodWeight = 2; // 单份食物重量
    private static final Integer weightMax = 1200; // 负重上限
    private static final Integer average = weightMax / (waterWeight + foodWeight); // 补给平衡值
    private static final Integer profit = 200; //挖矿收益
    private static final Integer terminationDate = 10; // 终止日期
    private static final Integer waterPrice = 5; // 单份水价格
    private static final Integer foodPrice = 10; // 单份食物价格

    private static final Integer sunnyWater = 3; // 晴天基础水消耗
    private static final Integer sunnyFood = 4; // 晴天基础食物消耗
    private static final Integer highWater = 9; // 高温基础水消耗
    private static final Integer highFood = 9;  // 高温基础食物消耗
    private static final Integer sandstormWater = 10; // 沙暴基础水消耗
    private static final Integer sandstormFood = 10;  // 沙暴基础食物消耗

    private static final Integer threshold = average; // 食物补给阈值
    private static final Integer balance = 0; // 食物-水 补给平衡参数

    private static List<Player.resourcesType> resourceList = Arrays.asList( // 天气分布
            sunny, high, sunny, sunny, sunny, sunny, high, high, high, high
    );

    /**
     * 天气-水基础消耗 映射表
     */
    private static HashMap<Player.resourcesType, Integer> waterConsume = new HashMap<Player.resourcesType, Integer>() {{
        put(sunny, sunnyWater);
        put(high, highWater);
        put(Sandstorm, sandstormWater);
    }};
    /**
     * 天气-食物基础消耗 映射表
     */
    private static HashMap<Player.resourcesType, Integer> foodConsume = new HashMap<Player.resourcesType, Integer>() {{
        put(sunny, sunnyFood);
        put(high, highFood);
        put(Sandstorm, sandstormFood);
    }};

    /**
     * 启动入口
     */
    public static void main(String[] args) {
        GameMap gameMap = initMap();
        Player player1 = new Player(initWaterNum, initFoodNum, initCapital - initWaterNum * waterPrice - initFoodNum * foodPrice, 0, gameMap, 0, 0);
        Player player2 = new Player(initWaterNum, initFoodNum, initCapital - initWaterNum * waterPrice - initFoodNum * foodPrice, 0, gameMap, 0, 0);
        play(player1, player2, gameMap, gameMap, new HashMap<Integer, Player>(), new HashMap<Integer, Player>(), false, false);
    }

    /**
     * 动态规划求解最优策略问题。
     *
     * @param player1  游戏玩家P1
     * @param player2  游戏玩家P2
     * @param gameMap1 P1玩家当前地图节点
     * @param gameMap2 P2玩家当前地图节点
     * @param path1    P1玩家行动路线
     * @param path2    P2玩家行动路线
     * @param end1     P2玩家是否抵达终点
     * @param end2     P2玩家是否抵达终点
     */
    private static void play(Player player1, Player player2, GameMap gameMap1, GameMap gameMap2, Map<Integer, Player> path1, Map<Integer, Player> path2, boolean end1, boolean end2) {
        if ((player1.getWaterNum() < 0 || player1.getFoodNum() < 0 || player2.getWaterNum() < 0 || player2.getFoodNum() < 0)) {
            return;
        }
        if (gameMap1.getMapType() == GameMap.type.village) {
            supply(average, player1, 0);
        }
        if (gameMap2.getMapType() == GameMap.type.village) {
            supply(average, player2, 0);
        }
        if (!end1) {
            path1.put(player1.getNowDate(), new Player(player1.getWaterNum(), player1.getFoodNum(), player1.getCapital(), gameMap1));
        }
        if (!end2) {
            path2.put(player2.getNowDate(), new Player(player2.getWaterNum(), player2.getFoodNum(), player2.getCapital(), gameMap2));
        }
        if (gameMap1.getMapType() == GameMap.type.end) {
            end1 = true;
        }
        if (gameMap2.getMapType() == GameMap.type.end) {
            end2 = true;
        }
        if (end1 && end2) {
            Integer capital1 = player1.getCapital();
            if (initFoodNum > player1.getFoodEat()) {
                capital1 += player1.getFoodNum() * foodPrice;
            } else {
                capital1 += player1.getFoodNum() * foodPrice * 2;
            }
            if (initWaterNum > player1.getWaterEat()) {
                capital1 += player1.getWaterNum() * waterPrice;
            } else {
                capital1 += player1.getWaterNum() * waterPrice * 2;
            }

            Integer capital2 = player2.getCapital();
            if (initFoodNum > player2.getFoodEat()) {
                capital2 += player2.getFoodNum() * foodPrice;
            } else {
                capital2 += player2.getFoodNum() * foodPrice * 2;
            }
            if (initWaterNum > player2.getWaterEat()) {
                capital2 += player2.getWaterNum() * waterPrice;
            } else {
                capital2 += player2.getWaterNum() * waterPrice * 2;
            }

            if (capital1 + capital2 > maxCapital) {
                System.out.println("---------------------------------------------------");
                System.out.printf("成功通关!\n玩家总收益:"+maxCapital+"\n玩家1：%-13s\t%-13s\t%-13s\n玩家2：%-13s\t%-13s\t%-13s\n", "旅途总收益=" + capital1, "旅途总水消耗=" + player1.getWaterEat(), "旅途总食物消耗=" + player1.getFoodEat(), "旅途总收益=" + capital2, "旅途总水消耗=" + player2.getWaterEat(), "旅途总食物消耗=" + player2.getFoodEat());
                maxCapital = capital1 + capital2;
                Integer waterNum = player1.getWaterNum();
                Integer foodNum = player1.getFoodNum();
                HashMap<Integer,Player> printPath = new HashMap<>(path1);
                for (int i = printPath.size()-1;i>=0;i--){
                    Player player11 = printPath.get(i);
                    printPath.put(i,new Player(player11.getWaterNum()-waterNum,player11.getFoodNum()-foodNum,capital1,player11.getMapId()));
                    if (player11.getMapId().getMapType()== GameMap.type.start){
                        break;
                    }
                }
                System.out.println("玩家1具体行程安排如下:");
                for (Integer day : printPath.keySet()) {
                    System.out.print("第" + (day) + "天:");
                    printPath.get(day).printPlayer();
                }
                waterNum = player2.getWaterNum();
                foodNum = player2.getFoodNum();
                HashMap<Integer,Player> printPath2 = new HashMap<>(path2);
                for (int i = printPath2.size()-1;i>=0;i--){
                    Player player22 = printPath2.get(i);
                    printPath2.put(i,new Player(player22.getWaterNum()-waterNum,player22.getFoodNum()-foodNum,capital2,player22.getMapId()));
                    if (player22.getMapId().getMapType()== GameMap.type.start){
                        break;
                    }
                }
                System.out.println("玩家2具体行程安排如下:");
                for (Integer day : printPath2.keySet()) {
                    System.out.print("第" + (day) + "天:");
                    printPath2.get(day).printPlayer();
                }
                System.out.println("---------------------------------------------------");
            }
            return;
        }
        if (player1.getNowDate().equals(terminationDate) || player2.getNowDate().equals(terminationDate)) {
            return;
        }
        Player.resourcesType resources = resourceList.get(Math.max(player1.getNowDate(), player2.getNowDate()));
        Integer waterBasics = waterConsume.get(resources);
        Integer foodBasics = foodConsume.get(resources);
        if (!end1 && !end2) {
            for (GameMap map1 : gameMap1.getAdjacent()) {
                for (GameMap map2 : gameMap2.getAdjacent()) {
                    Player newPlayer1;
                    Player newPlayer2;
                    if (gameMap1.getId().equals(gameMap2.getId()) && map1.getId().equals(map2.getId())) {
                        newPlayer1 = new Player(player1.getWaterNum() - (waterBasics * 4), player1.getFoodNum() - (foodBasics * 4), player1.getCapital(), player1.getNowDate() + 1, gameMap1, player1.getWaterEat() + waterBasics * 4, player1.getFoodEat() + foodBasics * 4);
                        newPlayer2 = new Player(player2.getWaterNum() - (waterBasics * 4), player2.getFoodNum() - (foodBasics * 4), player2.getCapital(), player2.getNowDate() + 1, gameMap2, player2.getWaterEat() + waterBasics * 4, player2.getFoodEat() + foodBasics * 4);
                    } else {
                        newPlayer1 = new Player(player1.getWaterNum() - (waterBasics * 2), player1.getFoodNum() - (foodBasics * 2), player1.getCapital(), player1.getNowDate() + 1, gameMap1, player1.getWaterEat() + waterBasics * 2, player1.getFoodEat() + foodBasics * 2);
                        newPlayer2 = new Player(player2.getWaterNum() - (waterBasics * 2), player2.getFoodNum() - (foodBasics * 2), player2.getCapital(), player2.getNowDate() + 1, gameMap2, player2.getWaterEat() + waterBasics * 2, player2.getFoodEat() + foodBasics * 2);
                    }
                    play(newPlayer1, newPlayer2, map1, map2, new HashMap<>(path1), new HashMap<>(path2), end1, end2);
                }
            }
            if (gameMap1.getMapType() == GameMap.type.mine && gameMap2.getMapType() == GameMap.type.mine) {
                Player newPlayer1 = new Player(player1.getWaterNum() - (waterBasics * 3), player1.getFoodNum() - (foodBasics * 3), player1.getCapital() + profit / 2, player1.getNowDate() + 1, gameMap1, player1.getWaterEat() + waterBasics * 3, player1.getFoodEat() + foodBasics * 3);
                Player newPlayer2 = new Player(player2.getWaterNum() - (waterBasics * 3), player2.getFoodNum() - (foodBasics * 3), player2.getCapital() + profit / 2, player2.getNowDate() + 1, gameMap2, player2.getWaterEat() + waterBasics * 3, player2.getFoodEat() + foodBasics * 3);
                play(newPlayer1, newPlayer2, gameMap1, gameMap2, new HashMap<>(path1), new HashMap<>(path2), end1, end2);
            }
            if (gameMap1.getMapType() == GameMap.type.mine) {
                Player newPlayer1 = new Player(player1.getWaterNum() - (waterBasics * 3), player1.getFoodNum() - (foodBasics * 3), player1.getCapital() + profit, player1.getNowDate() + 1, gameMap1, player1.getWaterEat() + waterBasics * 3, player1.getFoodEat() + foodBasics * 3);
                for (GameMap map : gameMap2.getAdjacent()) {
                    Player newPlayer2 = new Player(player2.getWaterNum() - (waterBasics * 2), player2.getFoodNum() - (foodBasics * 2), player2.getCapital(), player2.getNowDate() + 1, gameMap2, player2.getWaterEat() + waterBasics * 2, player2.getFoodEat() + foodBasics * 2);
                    play(newPlayer1, newPlayer2, gameMap1, map, new HashMap<>(path1), new HashMap<>(path2), end1, end2);
                }
                Player newPlayer2 = new Player(player2.getWaterNum() - (waterBasics), player2.getFoodNum() - (foodBasics), player2.getCapital(), player2.getNowDate() + 1, gameMap2, player2.getWaterEat() + waterBasics * 2, player2.getFoodEat() + foodBasics * 2);
                play(newPlayer1, newPlayer2, gameMap1, gameMap2, new HashMap<>(path1), new HashMap<>(path2), end1, end2);
            }
            if (gameMap2.getMapType() == GameMap.type.mine) {
                Player newPlayer2 = new Player(player2.getWaterNum() - (waterBasics * 3), player2.getFoodNum() - (foodBasics * 3), player2.getCapital() + profit, player2.getNowDate() + 1, gameMap2, player2.getWaterEat() + waterBasics * 3, player2.getFoodEat() + foodBasics * 3);
                for (GameMap map : gameMap1.getAdjacent()) {
                    Player newPlayer1 = new Player(player1.getWaterNum() - (waterBasics * 2), player1.getFoodNum() - (foodBasics * 2), player1.getCapital(), player1.getNowDate() + 1, gameMap1, player1.getWaterEat() + waterBasics * 2, player1.getFoodEat() + foodBasics * 2);
                    play(newPlayer1, newPlayer2, map, gameMap2, new HashMap<>(path1), new HashMap<>(path2), end1, end2);
                }
                Player newPlayer1 = new Player(player1.getWaterNum() - (waterBasics), player1.getFoodNum() - (foodBasics), player1.getCapital(), player1.getNowDate() + 1, gameMap1, player1.getWaterEat() + waterBasics * 2, player1.getFoodEat() + foodBasics * 2);
                play(newPlayer1, newPlayer2, gameMap1, gameMap2, new HashMap<>(path1), new HashMap<>(path2), end1, end2);
            }
            Player newPlayer1 = new Player(player1.getWaterNum() - (waterBasics), player1.getFoodNum() - (foodBasics), player1.getCapital(), player1.getNowDate() + 1, gameMap1, player1.getWaterEat() + waterBasics * 2, player1.getFoodEat() + foodBasics * 2);
            Player newPlayer2 = new Player(player2.getWaterNum() - (waterBasics), player2.getFoodNum() - (foodBasics), player2.getCapital(), player2.getNowDate() + 1, gameMap2, player2.getWaterEat() + waterBasics * 2, player2.getFoodEat() + foodBasics * 2);
            play(newPlayer1, newPlayer2, gameMap1, gameMap2, new HashMap<>(path1), new HashMap<>(path2), end1, end2);
        } else if (end1) {
            for (GameMap map : gameMap2.getAdjacent()){
                Player newPlayer = new Player(player2.getWaterNum() - (waterBasics * 2), player2.getFoodNum() - (foodBasics * 2), player2.getCapital(), player2.getNowDate() + 1, map, player2.getWaterEat() + waterBasics * 2, player2.getFoodEat() + foodBasics * 2);
                play(player1,newPlayer,gameMap1,map, new HashMap<>(path1), new HashMap<>(path2), end1, end2);
            }
            if (gameMap2.getMapType()==GameMap.type.mine){
                Player newPlayer = new Player(player2.getWaterNum() - (waterBasics * 3), player2.getFoodNum() - (foodBasics * 3), player2.getCapital(), player2.getNowDate() + 1, gameMap2, player2.getWaterEat() + waterBasics * 3, player2.getFoodEat() + foodBasics * 3);
                play(player1,newPlayer,gameMap1,gameMap2, new HashMap<>(path1), new HashMap<>(path2), end1, end2);
            }
            Player newPlayer = new Player(player2.getWaterNum() - (waterBasics), player2.getFoodNum() - (foodBasics), player2.getCapital(), player2.getNowDate() + 1, gameMap2, player2.getWaterEat() + waterBasics * 2, player2.getFoodEat() + foodBasics * 2);
            play(player1,newPlayer,gameMap1,gameMap2, new HashMap<>(path1), new HashMap<>(path2), end1, end2);
        } else {
            for (GameMap map : gameMap1.getAdjacent()){
                Player newPlayer = new Player(player1.getWaterNum() - (waterBasics * 2), player1.getFoodNum() - (foodBasics * 2), player1.getCapital(), player1.getNowDate() + 1, map, player1.getWaterEat() + waterBasics * 2, player1.getFoodEat() + foodBasics * 2);
                play(newPlayer,player2,map,gameMap2, new HashMap<>(path1), new HashMap<>(path2), end1, end2);
            }
            if (gameMap2.getMapType()==GameMap.type.mine){
                Player newPlayer = new Player(player1.getWaterNum() - (waterBasics * 3), player1.getFoodNum() - (foodBasics * 3), player1.getCapital(), player1.getNowDate() + 1, gameMap1, player1.getWaterEat() + waterBasics * 3, player1.getFoodEat() + foodBasics * 3);
                play(newPlayer,player2,gameMap1,gameMap2, new HashMap<>(path1), new HashMap<>(path2), end1, end2);
            }
            Player newPlayer = new Player(player1.getWaterNum() - (waterBasics), player1.getFoodNum() - (foodBasics), player1.getCapital(), player1.getNowDate() + 1, gameMap1, player1.getWaterEat() + waterBasics * 2, player1.getFoodEat() + foodBasics * 2);
            play(newPlayer,player2,gameMap1,gameMap2, new HashMap<>(path1), new HashMap<>(path2), end1, end2);
        }
    }

    /**
     * 游戏地图逻辑绘制
     *
     * @return 游戏地图起始节点
     */
    private static GameMap initMap() {
        GameMap m1 = new GameMap(1, null, GameMap.type.start);
        GameMap m3 = new GameMap(3, null, GameMap.type.land);
        GameMap m4 = new GameMap(4, null, GameMap.type.land);
        GameMap m5 = new GameMap(5, null, GameMap.type.land);
        GameMap m6 = new GameMap(6, null, GameMap.type.land);
        GameMap m7 = new GameMap(7, null, GameMap.type.land);
        GameMap m9 = new GameMap(9, null, GameMap.type.mine);
        GameMap m11 = new GameMap(11, null, GameMap.type.land);
        GameMap m12 = new GameMap(12, null, GameMap.type.land);
        GameMap m13 = new GameMap(13, null, GameMap.type.end);

        m1.setAdjacent(Arrays.asList(m4, m5));
        m5.setAdjacent(Arrays.asList(m6));
        m6.setAdjacent(Arrays.asList(m13));
        m7.setAdjacent(Arrays.asList(m12));
        m3.setAdjacent(Arrays.asList(m9));
        m4.setAdjacent(Arrays.asList(m3,m6,m7));
        m9.setAdjacent(Arrays.asList(m11));
        m11.setAdjacent(Arrays.asList(m13));
        m12.setAdjacent(Arrays.asList(m13));
        m13.setAdjacent(Arrays.asList());

        return m1;
    }

    /**
     * 村庄补给策略
     *
     * @param threshold 食物补给阈值
     * @param player    补给玩家
     * @param balance   补给平衡参数
     */
    private static void supply(int threshold, Player player, int balance) {
        if (player.getFoodNum() > threshold) {
            Integer waterNum = (weightMax - (player.getFoodNum() * foodWeight + player.getWaterNum() * waterWeight)) / waterWeight;
            player.setWaterNum(player.getWaterNum() + waterNum);
            ;
            player.setCapital(player.getCapital() - waterNum * waterPrice * 2);
        } else {
            Integer waterD = (average - (balance * waterWeight / foodWeight)) - player.getWaterNum();
            Integer foodD = (average + balance) - player.getFoodNum();
            if (waterD > 0) {
                player.setWaterNum(player.getWaterNum() + waterD);
                player.setCapital(player.getCapital() - waterD * waterPrice * 2);
            }
            if (foodD > 0) {
                player.setFoodNum(player.getFoodNum() + foodD);
                player.setCapital(player.getCapital() - foodD * foodPrice * 2);
            }
        }
    }
}
